package com.pinxixi.search.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.pinxixi.api.specification.SpecificationControllerApi;
import com.pinxixi.framework.domain.category.Category;
import com.pinxixi.framework.domain.goods.Item;
import com.pinxixi.framework.domain.specification.SpecificationOption;
import com.pinxixi.framework.domain.template.Template;
import com.pinxixi.framework.model.response.CommonCode;
import com.pinxixi.framework.model.response.ListResponseResult;
import com.pinxixi.framework.model.response.QueryResponseResult;
import com.pinxixi.framework.model.response.QueryResult;
import com.pinxixi.search.client.CategoryApiClient;
import com.pinxixi.search.client.SpecificationApiClient;
import com.pinxixi.search.client.TemplateApiClient;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class SearchService {

    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private CategoryApiClient categoryApiClient;

    @Autowired
    private TemplateApiClient templateApiClient;

    @Autowired
    private SpecificationApiClient specificationApiClient;

    public QueryResponseResult<Map> search(Integer page, String keyword,String params) throws IOException {
        Map paramsMap = JSONObject.parseObject(params, Map.class);


        List<Item> list = new ArrayList();
        Long total = 0L;

        //搜索请求对象
        SearchRequest searchRequest = new SearchRequest("pinxixi_shop");
        //指定类型
        searchRequest.types("doc");
        //搜索源构建对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        if(page==null){
            page = 1;
        }

        //每页记录数
        int size = 5;
        //计算出记录起始下标
        int from  = (page-1)*size;
        searchSourceBuilder.from(from);//起始记录下标，从0开始
        searchSourceBuilder.size(size);//每页显示的记录数


        //搜索方式
        //matchAllQuery搜索全部
        MultiMatchQueryBuilder multiMatchQueryBuilder = null;
        if(keyword!=null && !keyword.trim().equals("")){
            System.out.println(keyword);

            multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword, "name", "brand", "category")
                    .minimumShouldMatch("30%")
                    .field("name", 15)
                    .field("category", 8)
                    .field("brand", 5);

            //searchSourceBuilder.query(multiMatchQueryBuilder);
        }else{
            searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        }

        //设置源字段过虑,第一个参数结果集包括哪些字段，第二个参数表示结果集不包括哪些字段
        searchSourceBuilder.fetchSource(new String[]{"name","brand","category","spec","price","pic","timestamp"},new String[]{});


        //创建分组条件对象，设置分组字段，并给分组结果设置别名
        TermsAggregationBuilder categoryAggregationBuilder = AggregationBuilders.terms("terms_category").field("category");
        TermsAggregationBuilder brandAggregationBuilder = AggregationBuilders.terms("terms_brand").field("brand");
        searchSourceBuilder.aggregation(categoryAggregationBuilder);
        searchSourceBuilder.aggregation(brandAggregationBuilder);


        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(multiMatchQueryBuilder);

        //对前端传递过来的所有条件（paramsMap）做遍历
        Set keySet = paramsMap.keySet();
        Iterator iterator = keySet.iterator();
        while (iterator.hasNext()){
            String key = (String) iterator.next();
            String value = (String) paramsMap.get(key);

            //判断每一组参数的值是否为空，不为空给ES搜素加料
            if(value!=null && !value.equals("")){
                //定义过虑器
                boolQueryBuilder.filter(QueryBuilders.termQuery(key,value));
                //searchSourceBuilder.query(QueryBuilders.termQuery(key,value));
            }

        }

        searchSourceBuilder.query(boolQueryBuilder);

        //向搜索请求对象中设置搜索源
        searchRequest.source(searchSourceBuilder);

        //设置高亮字段和前后缀
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<hl>");
        highlightBuilder.postTags("</hl>");
        highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
        searchSourceBuilder.highlighter(highlightBuilder);

        //执行搜索,向ES发起http请求
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        //搜索结果
        SearchHits hits = searchResponse.getHits();
        //匹配到的总记录数

        TotalHits totalHits = hits.getTotalHits();
        total = totalHits.value;

        //得到匹配度高的文档
        SearchHit[] searchHits = hits.getHits();
        //日期格式化对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for(SearchHit hit:searchHits){

            //源文档内容
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();

            Item item = new Item();
            item.setId(Long.parseLong(hit.getId()));
            item.setTitle((String) sourceAsMap.get("name"));
            item.setPrice(new BigDecimal((Double) sourceAsMap.get("price")));
            item.setImage((String) sourceAsMap.get("pic"));


            //取出高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if(highlightFields!=null){
                //取出name高亮字段
                HighlightField nameHighlightField = highlightFields.get("name");
                if(nameHighlightField!=null){
                    Text[] fragments = nameHighlightField.getFragments();
                    StringBuffer stringBuffer = new StringBuffer();
                    for(Text text:fragments){
                        stringBuffer.append(text);
                    }
                    item.setTitle(stringBuffer.toString());
                }
            }



            list.add(item);
        }

        ParsedStringTerms categoryAggregation = searchResponse.getAggregations().get("terms_category");
        List<? extends Terms.Bucket> categoryBuckets = categoryAggregation.getBuckets();
        List categoryList  = new ArrayList();
        for (Terms.Bucket bucket: categoryBuckets) {
            String key = (String) bucket.getKey();
            categoryList.add(key);
        }

        ParsedStringTerms brandAggregation = searchResponse.getAggregations().get("terms_brand");
        List<? extends Terms.Bucket> brandBuckets = brandAggregation.getBuckets();
        List brandList  = new ArrayList();
        for (Terms.Bucket bucket: brandBuckets) {
            String key = (String) bucket.getKey();
            brandList.add(key);
        }

        HashMap resultMap = new HashMap<>();
        resultMap.put("list",list);
        resultMap.put("total",total);
        resultMap.put("categoryList",categoryList);
        resultMap.put("brandList",brandList);

        //根据分类的第一项查询对应的规格及参数
        if(categoryList.size()>0){
            String categoryName = (String) categoryList.get(0);

            //根据分类名称，远程调用CategoryApi获取分类信息
            QueryResponseResult<Category> categoryResult = categoryApiClient.findByName(categoryName);
            Category category = categoryResult.getEntity();
            //从分类对象中取出模版ID
            Integer typeId = category.getTypeId();
            QueryResponseResult<Template> templateResult = templateApiClient.findOne(typeId);
            Template template = templateResult.getEntity();
            //从模版信息中取出规格信息
            String specIds = template.getSpecIds();
            //将JSON数组字符串---》List
            List<Map> specList = JSONArray.parseArray(specIds, Map.class);
            for(Map spec:specList){
                ListResponseResult<SpecificationOption> specResult = specificationApiClient.findOptionsBySpecId((Integer) spec.get("id"));
                List optionList = specResult.getQueryResult().getList();
                spec.put("options",optionList);
            }
            resultMap.put("specList",specList);
        }

        return new QueryResponseResult<Map>(CommonCode.SUCCESS,resultMap);
    }

}
